﻿using GalaSoft.MvvmLight;
using RestaurantBooker.Web.Services;
using RestaurantBooker.Model;
using System.Collections.Generic;
using System;
using System.Linq;
using System.Windows.Controls;
using RestaurantBooker.Helpers;
using RestaurantBooker.View;
using System.Windows;
using GalaSoft.MvvmLight.Command;
using System.Windows.Media.Animation;
using RestaurantBooker.Model.Enums;

namespace RestaurantBooker.ViewModel
{
    /// <summary>
    /// This class contains properties that the main View can data bind to.
    /// <para>
    /// Use the <strong>mvvminpc</strong> snippet to add bindable properties to this ViewModel.
    /// </para>
    /// <para>
    /// You can also use Blend to data bind with the tool's support.
    /// </para>
    /// <para>
    /// See http://www.galasoft.ch/mvvm/getstarted
    /// </para>
    /// </summary>
    public class MainViewModel : ViewModelBase
    {
        #region Private members

        private List<FrameDescription> _frameDescriptions = new List<FrameDescription>();

        #endregion


        #region Properties (with theirs members)       

        /// <summary>
        /// The <see cref="CurrentMainPage" /> property's name.
        /// </summary>
        public const string CurrentMainPagePropertyName = "CurrentMainPage";

        private FrameDescription _currentMainPage = null;

        /// <summary>
        /// Gets the CurrentMainPage property.
        /// TODO Update documentation:
        /// Changes to that property's value raise the PropertyChanged event. 
        /// This property's value is broadcasted by the Messenger's default instance when it changes.
        /// </summary>
        public FrameDescription CurrentMainPage
        {
            get
            {
                return _currentMainPage;
            }

            set
            {
                if (_currentMainPage == value)
                {
                    return;
                }
                _currentMainPage = value;
                // Update bindings, no broadcast
                RaisePropertyChanged(CurrentMainPagePropertyName);
            }
        }


        /// <summary>
        /// The <see cref="HeaderVisibility" /> property's name.
        /// </summary>
        public const string HeaderVisibilityPropertyName = "HeaderVisibility";

        private Visibility _headerVisibility = Visibility.Collapsed;

        /// <summary>
        /// Gets the HeaderVisibility property.
        /// TODO Update documentation:
        /// Changes to that property's value raise the PropertyChanged event. 
        /// This property's value is broadcasted by the Messenger's default instance when it changes.
        /// </summary>
        public Visibility HeaderVisibility
        {
            get
            {
                return _headerVisibility;
            }

            set
            {
                if (_headerVisibility == value)
                {
                    return;
                }

                _headerVisibility = value;

                // Update bindings, no broadcast
                RaisePropertyChanged(HeaderVisibilityPropertyName);
            }
        }

        private Control _activeTabContent;
        public Control ActiveTabContent
        {
            get
            {
                return _activeTabContent;
            }

            private set
            {
                _activeTabContent = value;
                if (!(_activeTabContent is LoginPage))
                    HeaderVisibility = Visibility.Visible;
                else
                    HeaderVisibility = Visibility.Collapsed;
                RaisePropertyChanged("ActiveTabContent");
            }
        }

        public String MainAppVersion
        {
            get { return VersionHelper.Version; }
        }

        public String AppName
        {
            get { return "Restaraunt Booker"; }
        }




        /// <summary>
        /// The <see cref="RommVisuzlizerVM" /> property's name.
        /// </summary>
        //public const string RommVisuzlizerVMPropertyName = "RommVisuzlizerVM";

        //private RoomVisualizerViewModel _rommVisuzlizerVM = null;

        ///// <summary>
        ///// Gets the RommVisuzlizerVM property.
        ///// TODO Update documentation:
        ///// Changes to that property's value raise the PropertyChanged event. 
        ///// This property's value is broadcasted by the Messenger's default instance when it changes.
        ///// </summary>
        //public RoomVisualizerViewModel RommVisuzlizerVM
        //{
        //    get
        //    {
        //        return _rommVisuzlizerVM;
        //    }

        //    set
        //    {
        //        if (_rommVisuzlizerVM  == value)
        //        {
        //            return;
        //        }
        //        var oldValue = _rommVisuzlizerVM;
        //        _rommVisuzlizerVM  = value;

        //        // Update bindings, no broadcast
        //        RaisePropertyChanged(RommVisuzlizerVMPropertyName);
        //    }
        //}

        #endregion



        /// <summary>
        /// Initializes a new instance of the MainViewModel class.
        /// </summary>
        public MainViewModel()
        {
            if (IsInDesignMode)
            {
                // Code runs in Blend --> create design time data.
            }
            else
            {
                // Code runs "for real"
                // RommVisuzlizerVM = new RoomVisualizerViewModel();                                
            }

            InitializaFrameDescriptions();
            InitializeCommands();
        }



        #region [ Commands ]

        private void InitializeCommands()
        {
            OpenFrame = new RelayCommand<string>((cmdParam) =>
            {
                if (String.IsNullOrEmpty(cmdParam))
                    return;

                FrameType ft;
                if (!Enum.TryParse<FrameType>(cmdParam, out ft))
                {
                    throw new ArgumentException("Invalid OpenFrame command parameter!", cmdParam);
                }

                OpenFrameByFrameDescription(GetFrameDescriptionByFrameType(ft));
            });

            LogoutCommand = new RelayCommand<String>(
                (prm) =>
                {

                    // cleaning up opened frames
                    Cleanup();

                    // do logout
                    ServiceProcessor.ServiceProcessor.Instance.LogoutBooker();

                    OpenFrame.Execute("LoginPage");

                });

            DisplaySettingsCommand = new RelayCommand(() =>
                { 
                    MessageDlg.ShowInformation("Sorry, the settings dialog is not implemented yet"); }
                );
        }

        public RelayCommand<string> OpenFrame
        {
            get;
            private set;
        }


        public RelayCommand<String> LogoutCommand
        {
            get;
            private set;
        }


        public RelayCommand DisplaySettingsCommand
        {
            get;
            private set;
        }

        #endregion




        private void AppearAnimation(Control control, bool show, double secondDuration)
        {
            if (control == null) return;

            double opacityValue = 1;
            if (!show)
                opacityValue = 0;

            // Create a duration    
            Duration duration = new Duration(TimeSpan.FromSeconds(secondDuration));

            // Create two DoubleAnimations and set their properties.
            DoubleAnimation myDoubleAnimation = new DoubleAnimation();

            myDoubleAnimation.Duration = duration;

            //myDoubleAnimation.EasingFunction = new ExponentialEase() { EasingMode = EasingMode.EaseOut, Exponent = 2 };

            Storyboard sb = new Storyboard();
            sb.Duration = duration;
            sb.Children.Add(myDoubleAnimation);
            Storyboard.SetTarget(myDoubleAnimation, control);
            Storyboard.SetTargetProperty(myDoubleAnimation, new PropertyPath("Opacity"));
            myDoubleAnimation.To = opacityValue;
            // Begin the animation.
            sb.Begin();
        }


        public override void Cleanup()
        {



            // clean up all other frames
            CleanupFrames();

            base.Cleanup();
        }

        private void CleanupFrames()
        {
            ViewModelLocator mainLocator = App.Current.Resources["Locator"] as ViewModelLocator;
            if (mainLocator != null)
            {
                mainLocator.ClearFrames();
            }
        }


        #region Frames

        internal void OpenFrameByFrameDescription(FrameDescription frameDescription)
        {
            if (CurrentMainPage != frameDescription)
            {
                 CurrentMainPage = frameDescription;
                
                if (frameDescription.ControlInstance == null)
                {
                    frameDescription.ControlInstance = Activator.CreateInstance(frameDescription.ControlType) as Control;
                }

                frameDescription.ControlInstance.Opacity = 0;
                ActiveTabContent = frameDescription.ControlInstance;
                AppearAnimation(frameDescription.ControlInstance, true, 0.6);
            }
        }

        private FrameDescription GetFrameByType(List<FrameDescription> list, Type typeOfFrameControl)
        {
            foreach (var fd in list)
            {
                if (fd.ControlType == typeOfFrameControl)
                    return fd;
            }
            return null;
        }

        internal FrameDescription GetFramByType(Type typeOfFrameControl)
        {
            return GetFrameByType(_frameDescriptions, typeOfFrameControl);
        }

        internal void OpenFrameByFrameType(FrameType frameType)
        {            
            OpenFrameByFrameDescription(GetFrameDescriptionByFrameType(frameType));            
        }

        private FrameDescription GetFrameDescriptionByFrameType(FrameType frameType)
        {
            var frameDescription = _frameDescriptions.FirstOrDefault(fd => fd.FrameType == frameType);
            if (frameDescription == null)
                throw new ArgumentException("Not registered frame type description!", frameType.ToString());
            return frameDescription;
        }

        private void InitializaFrameDescriptions()
        {
            _frameDescriptions.Add(new FrameDescription() { FrameType = FrameType.LoginPage, ControlType = typeof(LoginPage), DisplayText = "Login" });
            _frameDescriptions.Add(new FrameDescription() { FrameType = FrameType.BookerPage, ControlType = typeof(BookerPage), DisplayText = "Booker mode - make orders" });
            _frameDescriptions.Add(new FrameDescription() { FrameType = FrameType.AdminPage, ControlType = typeof(AdminPage), DisplayText = "Admin mode - edit dictionaries" });
        }

               
        #endregion
    }
}